home *** CD-ROM | disk | FTP | other *** search
/ Workbench Add-On / Workbench Add-On - Volume 1.iso / BBS-Archive / DiskUtil / Misc / BTNTape.lha / src / btn.c next >
C/C++ Source or Header  |  1994-04-03  |  38KB  |  1,000 lines

  1. /****     BTNtape Handler for SCSI tape drives        ****/
  2. /**** Author: Bob Rethemeyer  (drBob@cup.portal.com)  ****/
  3.  
  4. #define VERSDATE "(03.14.94)"  /* (DD.MM.YY) */
  5. #define VERSNAME "BTNtape-RAR 3.0 "
  6. /* (If you recompile BTN, ^^^ put your initials here!) */
  7.  
  8. /*--------------------------------------------------------------------------
  9.  *  (c) Copyright 1990,1994  Robert Rethemeyer.
  10.  *  This software may be freely distributed and redistributed,
  11.  *  for non-commercial purposes, provided this notice is included.
  12.  *--------------------------------------------------------------------------
  13.  *  BTNtape is an AmigaDOS device handler to make a simple DOS TAPE: device.
  14.  *  It converts DOS packets for the device into I/O requests to a
  15.  *  "SCSI-direct" compatible device driver.  It is based on "my.handler"
  16.  *  by Phillip Lindsay and a SCSI-direct program by Robert Mitchell.
  17.  *  Seek() handling derived from code by Dennis J. Brueni, 8-24-91.
  18.  *  Source is ANSI C compliant.  Compile with SAS/C v6.
  19.  *
  20.  *  This handler works in conjunction with the accompanying TapeMon program.
  21.  *--------------------------------------------------------------------------
  22.  */
  23. #define _STRICT_ANSI
  24. #include <exec/types.h>
  25. #include <exec/nodes.h>
  26. #include <exec/lists.h>
  27. #include <exec/ports.h>
  28. #include <exec/tasks.h>
  29. #include <exec/libraries.h>
  30. #include <exec/io.h>
  31. #include <exec/memory.h>
  32. #include <devices/scsidisk.h>
  33. #include <intuition/intuition.h>
  34. #include <libraries/dos.h>
  35. #include <libraries/dosextens.h>
  36. #include <libraries/filehandler.h>
  37. #include <stdio.h>
  38. #include <string.h>
  39. #include <stdlib.h>
  40. #include <stdarg.h>
  41. #include "btn.h"
  42. #include "tplink.h"
  43.  
  44. #include <proto/exec.h>
  45. #include <proto/intuition.h>
  46. #include <pragmas/exec_pragmas.h>
  47. #include <pragmas/intuition_pragmas.h>
  48.  
  49. /* sense keys of interest */
  50. #define NOS 0x00  /* no sense */
  51. #define RCV 0x01  /* recovered error */
  52. #define ILL 0x05  /* illegal request */
  53. #define UAT 0x06  /* unit attention */
  54. #define BLC 0x08  /* blank check */
  55. #define VOF 0x0d  /* volume overflow */
  56. /* pseudo sense keys returned by DoSense */
  57. #define FMK 0x10  /* filemark */
  58. #define EOM 0x11  /* end of tape */
  59. #define ILI 0x12  /* incorr leng */
  60. #define SERR 0x13 /* SCSI error  */
  61.  
  62. /*======== Global data */
  63.  
  64. struct ExecBase *SysBase;
  65. struct IntuitionBase *IntuitionBase=NULL;
  66. UBYTE           *cdb;          /* pointer to SCSI command buffer */
  67. UBYTE           *sns;          /* pointer to sense data buffer   */
  68. UBYTE           *inq;          /* pointer to inquiry data        */
  69. struct SCSICmd  *cmd;          /* pointer to scsidirect command  */
  70. struct IOStdReq *ior;          /* pointer to io request structure*/
  71. UBYTE *TapeBuff[2]={NULL,NULL};/* pointers to 2 tape buffers     */
  72. struct Process  *myproc;       /* ptr to handler's process struct*/
  73. struct DosPacket *mypkt;       /* ptr to dos packet sent         */
  74. struct tplink   *linktp;       /* pointer to link structure      */
  75. struct MsgPort  *monport=NULL; /* ptr to mon communication msg port */
  76. struct MsgPort  *btnport;      /* ptr to btn communication msg port */
  77. struct BTNmsg    monmsg;       /* message we will send to tapemon*/
  78. ULONG  blknum;                 /* block number for io operation  */
  79. ULONG  blksize = 512;          /* bytes per tape block           */
  80. ULONG  numblks = 1;            /* number of blocks per io oper   */
  81. ULONG  TBSize;                 /* bytes in a tape buffer         */
  82. ULONG  rwlen;                  /* bytes in a tape read/write     */
  83. ULONG  rdlen;                  /* bytes/blks in a read           */
  84. ULONG  expect;                 /* expect num of bytes transferred*/
  85. ULONG  bugmask = 0;            /* 2090A bug circumvention        */
  86. long   filenum = -1;           /* current file position on tape  */
  87. long   lastwrtn= -1;           /* last filenum written           */
  88. long   actual;                 /* actual num of bytes transferred*/
  89. long   res1;                   /* #bytes xfered packet return    */
  90. long   Boff;                   /* current offset in tape buffer  */
  91. long   tpsize ;                /* tape size in blocks            */
  92. long   reserved = 0;           /* number of reserved blks at BOT */
  93. int    tapenum;                /* tape volume number             */
  94. BOOL   inprog = FALSE;         /* io operation in progress flag  */
  95. BOOL   reten = FALSE;          /* retension-on-UAT flag          */
  96. BOOL   mbusy = FALSE;          /* waiting for reply from tapemon */
  97. BOOL   bswap = FALSE;          /* swap-bytes flag */
  98. BOOL   dbug = FALSE;           /* print debug info */
  99. char   *z;                     /* scratch */
  100. char   *devname, nbuf[32];     /* file name reference from Open()*/
  101. char   dbb[80];                /* buffer for monitor messages    */
  102. UBYTE  Lun = 0;                /* Logical Unit number << 5       */
  103. UBYTE  varblk = 0;             /* variable-block flag for SEQ    */
  104. UBYTE  fixedbit = 0;           /* fixed-block mode bit for SEQ   */
  105. UBYTE versiontag[] = "$VER: " VERSNAME VERSDATE;
  106. char  *cpywr = "(c) Copyright 1990-1994 R. Rethemeyer";
  107.  
  108. /***********************  Main program  ********************************/
  109. void _main(char *nu)
  110. {
  111.  struct SCSICmd scsicmd;  /* SCSIdirect command buffer */
  112.  UBYTE  snsarea[64];      /* sense data buffer */
  113.  UBYTE  cmdbuff[16];      /* SCSI command buffer */
  114.  UBYTE  inqdata[40];      /* inquiry data buffer */
  115.  struct tplink      tpl;       /* structure to link hndlr & mon     */
  116.  struct DeviceNode  *mynode;   /* ptr to devnode passed in pkt Arg3 */
  117.  struct MsgPort     *mp;       /* ptr to io message port            */
  118.  struct MsgPort     *pktport;  /* ptr to process message port       */
  119.  struct Message     *m;
  120.  struct BTNmsg      *b;
  121.  ULONG   dvnode;        /* ptr to devnode passed in pkt Arg3 */
  122.  ULONG   unit=99999;    /* device SCSI unit address          */
  123.  ULONG   bufmemtype=0;  /* type of mem for dynamic buffers   */
  124.  ULONG   gotopos=0;     /* position to open at (blk/filemark)*/
  125.  ULONG   fmarks=1;      /* # file marks for write/skip       */
  126.  ULONG   pktsig;        /* signal mask for process msg port  */
  127.  ULONG   btnsig=0;      /* signal mask for mon comm msg port */
  128.  char    *driver;       /* name of SCSI device driver        */
  129.  UBYTE   *dptr;         /* ptr to next byte in dos buffer    */
  130.  long    dflags=0;      /* device flags                      */
  131.  long    iostat;        /* status of previous read           */
  132.  long    dcnt;          /* count of dos packet bytes to move */
  133.  long    mcnt;          /* count of bytes to move            */
  134.  long    rcnt;          /* count of bytes moved for read     */
  135.  long    current;       /* current byte position in file     */
  136.  long    remain;        /* bytes remaining in tape buffer    */
  137.  long    pkcnt;         /* flag to indicate data written     */
  138.  long    x;
  139.  int     y=0;
  140.  int     opmode=0;      /* how has the handler been opened   */
  141.     #define CLOSED  0       /* not doing anything */
  142.     #define READING 1       /* reading tape */
  143.     #define WRITING 2       /* writing tape */
  144.     #define CURRPOS 3       /* read current file position */
  145.     #define RAWMODE 4       /* raw command mode */
  146.     #define UMODSEL 5       /* user mode select */
  147.  int     Bn;            /* current buffer number, 0 or 1 */
  148.  int     position;      /* how to position tape at Open  */
  149.     #define POSREW  0       /* rewind to beginning */
  150.     #define POSNREW 1       /* don't move          */
  151.     #define POSAPP  2       /* append to eod (seq) */
  152.     #define POSEND  3       /* append to eod (seq) */
  153.     #define POSSKIP 4       /* skip to file/block  */
  154.  int     nrflag=POSREW; /* rewind behavior of TAPE: */
  155.  BOOL    rewcl = FALSE; /* rewind-on-close flag */
  156.  BOOL    eject = FALSE; /* eject-on-close flag  */
  157.  BOOL    dirty = FALSE; /* buffer has unwritten data in it */
  158.  BOOL    atend;         /* flags filemark encountered */
  159.  BOOL    eot;           /* flags end-of-tape encountered */
  160.  BOOL    rderr;         /* flags error during read */
  161.  BOOL    errec = FALSE; /* attempt error recovery flag */
  162.  BOOL    aonly = FALSE; /* append-only flag */
  163.  BOOL    rdonly =FALSE; /* read-only flag */
  164.  BOOL    starting=FALSE;/* flag detects first open message */
  165.  BOOL    dos2=FALSE;    /* indicates if later OS version */
  166.  int     Ctl=CTLIMM;    /* Controls overlapped dos/scsi I/O */
  167.  char   *termn8 = "     TERMINATE BTN     ";
  168.  char    portname[32] = "BTN_";
  169.  char    monpname[32] = "TMON_";
  170.  
  171. /*======== Startup */
  172.  
  173.  SysBase = *((struct ExecBase **) (4));
  174.  IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",0);
  175.  myproc = (struct Process *) FindTask(0L);          /* find this process */
  176.  pktport= &myproc->pr_MsgPort;
  177.  pktsig = 1L << pktport->mp_SigBit;
  178.  WaitPort(pktport);                              /* wait for startup packet */
  179.  m = GetMsg(pktport);
  180.  mypkt = PKTADR(m);
  181.  /* packet: Arg1=BSTR to name, Arg2=BSTR to startup string, Arg3=BPTR devnode*/
  182.  mynode = (struct DeviceNode *) BADDR(mypkt->dp_Arg3);
  183.  dvnode = (ULONG) mypkt->dp_Arg3;
  184.  
  185. /*======== Create linkage block for tapemon.
  186. */
  187.  tpl.cpyw    = cpywr;
  188.  tpl.version = "XXXXX " VERSNAME "(" __DATE__ ")";
  189.  tpl.devnode = (void *)mynode;
  190.  tpl.dbb     = dbb;
  191.  tpl.unit    = &unit;
  192.  tpl.Lun     = &Lun;
  193.  tpl.sense   = NULL;
  194.  tpl.badparm = FALSE;
  195.  linktp = &tpl;
  196.  
  197. /*======== Extract info from mountlist Startup parameter. It may be
  198.   ======== enclosed in quotes, and each item is separated by a single '/'.
  199.   ======== First item is always the driver name, others may be in any order.
  200. */
  201.  z = (char *)BADDR(mypkt->dp_Arg2)+1 ;  /* Arg2= BSTR to mountlist 'Startup'*/
  202.  if(*z=='\"')  {                      /* remove quotes if any */
  203.      z++;
  204.      z[strlen(z)-1]= '\0' ;
  205.  }
  206.  driver = tpl.driver = z;
  207.  for(; *z != '/'; z++);
  208.  *(z++) = '\0';
  209.  toUPPER(z);
  210.  while (y!=100) {
  211.    switch(y=getstart(&x)) {
  212.      case 0:  /* UN */  unit = (ULONG) x;  break;
  213.      case 1:  /* BS */  blksize = (ULONG) x;  break;
  214.      case 2:  /* NB */  numblks = (ULONG) x;  break;
  215.      case 3:  /* RB */  reserved = x;  break;
  216.      case 4:  /* BT */  bufmemtype = (ULONG) x;  break;
  217.      case 5:  /* FM */  fmarks = x & 0xff;  break;
  218.      case 6:  /* ER */  errec = (x!=0);  break;
  219.      case 7:  /* OV */  if(!x) Ctl = CTLWAIT; break;
  220.      case 8:  /* DF */  dflags = x; break;
  221.      case 9:  /* C9 */  if(x) bugmask = 0x01000000; break;
  222.      case 10: /* NR */  nrflag = x ? POSNREW : POSREW; break;
  223.      case 11: /* RT */  reten = (x!=0); break;
  224.      case 12: /* RC */  rewcl = (x!=0); break;
  225.      case 13: /* EC */  eject = (x!=0); break;
  226.      case 14: /* SW */  bswap = (x!=0); break;
  227.      case 15: /* RO */  rdonly= (x!=0); break;
  228.      case 16: /* AO */  aonly = (x!=0); break;
  229.      case 17: /* MR */  starting = (x!=0); break;
  230.      case 18: /* DB */  dbug  = (x!=0); break;
  231.  /***case -:   * VB *   if(x) varblk = 1;   break;  sorry, vb mode disabled */
  232.      case 99: /* ?? */  tpl.badparm = TRUE; break;
  233.      default: ;
  234.    }
  235.  }
  236.  Lun = (UBYTE) unit;    Lun = ((Lun/10-(Lun/100)*10) & 7) << 5;
  237.  rwlen = TBSize = numblks * blksize;   /* size of a tape buffer */
  238.  
  239.  cdb = cmdbuff;
  240.  sns = snsarea;
  241.  inq = tpl.inquiry = inqdata;
  242.  cmd = &scsicmd;
  243.  ior = (struct IOStdReq *) CreateExtIO( mp=CreatePort(NULL,0),
  244.                                          sizeof(struct IOStdReq));
  245.  
  246. /*======== Open the SCSIdirect device */
  247.  
  248.  if ( OpenDevice(driver,unit,(struct IORequest *)ior,dflags)  )  {
  249.     ask("Can't open SCSI-direct",termn8,NULL);
  250.     LEAVE:
  251.     returnpkt(DOSFALSE,ERROR_INVALID_COMPONENT_NAME);
  252.     if(ior) DeleteExtIO((struct IORequest *)ior);
  253.     if(mp) DeletePort(mp);
  254.     CloseLibrary((struct Library *)IntuitionBase);
  255.     return;
  256.  }
  257.  
  258. /*======== Create the port that TapeMon will
  259.   ======== use to acknowledge Mprintf() requests.
  260. */
  261.  if( btnport = CreatePort(
  262.                strcat(portname,myproc->pr_Task.tc_Node.ln_Name), 0) )
  263.  {
  264.     btnsig = 1L << btnport->mp_SigBit;
  265.     monmsg.bmsg.mn_ReplyPort = btnport;
  266.  
  267.     /*======== If TapeMon is already running, send it a wakeup message. */
  268.     Forbid();
  269.     if(monport = FindPort(strcat(monpname,myproc->pr_Task.tc_Node.ln_Name)) )
  270.        PutMsg(monport,&monmsg.bmsg);
  271.     Permit();
  272.     if(monport) { WaitPort(btnport);  GetMsg(btnport); }
  273.     monport = NULL;
  274.  }
  275.  
  276. /*======= Find the SCSI device type by INQUIRY.  Sequential or direct access?
  277. */
  278.  if(x=TapeIO(INQUIRY,0,CTLWAIT)) {
  279.     if(x <= HFERR_SelTimeout) {
  280.        ask("Can't select tape drive",termn8,NULL);
  281.        goto LEAVE;
  282.     }
  283.     TapeIO(TSENSE,0,CTLWAIT);
  284.     inq[0] = 0x01; /* assume SEQ */
  285.     inq[8] = 0;
  286.  }
  287.  inq[36] = 0;  /* null-terminate vendor info */
  288.  if(SEQ) {
  289.     reserved = 0;
  290.     fixedbit = varblk ^ 0x01;  /* fixed or variable block mode */
  291.     rdlen = (fixedbit) ? numblks : TBSize;
  292.  }
  293.  else if(DAC) rdlen = numblks;
  294.  else { ask("UNit is not a tape drive",termn8,NULL); goto LEAVE; }
  295.  
  296.  mynode->dn_Task = &myproc->pr_MsgPort;    /* install handler taskid */
  297.  returnpkt(DOSTRUE,0);                     /* reply to initial packet */
  298.  
  299. /* =========== The main packet processing loop =============== */
  300.  
  301.  for(;;)  {
  302.    if( m = GetMsg(pktport) )             /* Check for packet from DOS */
  303.       mypkt = PKTADR(m);
  304.    else if( m = GetMsg(btnport) ) {      /* Check for message from TapeMon */
  305.       if(m->mn_Node.ln_Type == NT_MESSAGE) {
  306.          b = (struct BTNmsg *)m;
  307.          /* Tapemon will send addr of port if it is starting up,
  308.             or NULL if it is shutting down. */
  309.          if(monport && b->mptr) b->mptr = NULL;  /* reject extra TapeMon */
  310.          else { monport = b->mptr;  b->mptr = linktp; }
  311.          ReplyMsg(m);
  312.          if(!monport) mbusy=FALSE;
  313.       }
  314.       else /*NT_REPLYMSG*/  mbusy=FALSE;
  315.       continue;
  316.    }
  317.    else {   /* Nothing to do but Wait() */
  318.       Wait(pktsig | btnsig);
  319.       continue;
  320.    }
  321.  
  322. #ifdef DBUG
  323.    if(dbug) Mprintf("PACKET=%d %08X %08X %08X\n", mypkt->dp_Type,
  324.                     mypkt->dp_Arg1, mypkt->dp_Arg2, mypkt->dp_Arg3);
  325. #endif
  326.  
  327.    switch(mypkt->dp_Type) {
  328.  
  329.      case ACTION_FINDINPUT:  /*----------- Open() ------------*/
  330.      case ACTION_FINDOUTPUT:
  331.      case ACTION_FINDUPDATE:
  332.           if(opmode != CLOSED)  {
  333.              returnpkt(DOSFALSE,ERROR_OBJECT_IN_USE);
  334.              Mprintf("Already open\n");
  335.              break;
  336.           }
  337.  
  338.           /* Determine open mode */
  339.           z = (char *)BADDR(mypkt->dp_Arg3);
  340.           x = (UBYTE)(*z);
  341.           devname = (char *)memcpy(nbuf, z+1, x);
  342.           *(devname+x) = '\0';
  343.           toUPPER(devname);
  344.           z = devname;
  345.           if(strchr(devname,':')) while(*(z++)!=':');  /*thx Tero Manninen*/
  346.           if     (!strcmp(z,"RAWCMD"))  opmode=RAWMODE;
  347.           else if(!strcmp(z,"MODESEL")) opmode=UMODSEL;
  348.           else if(!strcmp(z,"POS"))     opmode=CURRPOS;
  349.           else {    /* normal read/write */
  350.              opmode = (mypkt->dp_Type==ACTION_FINDINPUT) ? READING : WRITING;
  351.              if     (!strcmp(z,"*"))   position=POSNREW; /* current position */
  352.              else if(!strcmp(z,"NR"))  position=POSNREW; /* current position */
  353.              else if(!strcmp(z,"R"))   position=POSREW;  /* beginning of tape */
  354.              else if(!strcmp(z,"APP")) position=POSAPP;  /* append at end */
  355.              else if(!strcmp(z,"END")) position=POSEND;  /* append at end,slow*/
  356.              else if(*z>='0' && *z<='9') {               /* specific position */
  357.                    gotopos = strtoul(z,NULL,0);
  358.                    position=POSSKIP;
  359.              }
  360.              else if(*z=='\0') position = nrflag;  /* TAPE: */
  361.              else goto BADNAME;
  362.           }
  363.  
  364.           if(starting) goto FASTOPEN; /* skip stuff on first open from mount */
  365.  
  366.           /* detect illegal combinations */
  367.           if((opmode>=RAWMODE && mypkt->dp_Type==ACTION_FINDINPUT)
  368.           || (opmode==CURRPOS && mypkt->dp_Type!=ACTION_FINDINPUT)
  369.           || (opmode==READING && (position==POSAPP||position==POSEND))
  370.           || (SEQ && opmode==WRITING && position==POSSKIP)
  371.           || (DAC && (position==POSEND||position==POSAPP)) )
  372.           { BADNAME:
  373.              Mprintf("Invalid open name/combo %s\n",devname);
  374.             NOOPEN:
  375.              freestuff();
  376.              opmode=CLOSED;
  377.              returnpkt(DOSFALSE,ERROR_ACTION_NOT_KNOWN);
  378.              break;
  379.           }
  380.           if(SEQ && aonly && opmode==WRITING) {
  381.              if((position==POSNREW && lastwrtn>=0)
  382.              ||  position==POSAPP
  383.              ||  position==POSEND) ;
  384.              else { Mprintf("%s is append-only\n",devname);
  385.                     goto NOOPEN; }
  386.           }
  387.           if(opmode==WRITING && rdonly) {
  388.              Mprintf("%s is read-only\n",devname);
  389.              goto NOOPEN;
  390.           }
  391.  
  392.           /* Allocate storage for buffers */
  393.           TapeBuff[0] = (UBYTE *) AllocMem(TBSize, bufmemtype | MEMF_CLEAR);
  394.           TapeBuff[1] = (UBYTE *) AllocMem(TBSize, bufmemtype | MEMF_CLEAR);
  395.           if (!TapeBuff[0] || !TapeBuff[1]) {
  396.                freestuff();
  397.                opmode=CLOSED;
  398.                returnpkt(DOSFALSE,ERROR_NO_FREE_STORE);
  399.                Mprintf("Can't get %lu bytes for tape buffers\n",TBSize*2);
  400.                break;
  401.           }
  402.  
  403.           if(opmode<=WRITING) {
  404.  
  405.             /* Check device ready */
  406.             if(x=TapeIO(TREADY,0,CTLWAIT))  {
  407.                y=DoSense(x);
  408.                if(y==UAT) {
  409.                  blknum = filenum = reserved;  lastwrtn = -1;
  410.                  if(reten) if(x=TapeIO(TRETEN,0,CTLWAIT)) DoSense(x);
  411.                }
  412.                else if(y==NOS) ;
  413.                else if(y==ILL) ;
  414.                else {
  415.                GIVEUP:
  416.                  freestuff();
  417.                  opmode=CLOSED;
  418.                  filenum = lastwrtn = -1;
  419.                  returnpkt(DOSFALSE,ERROR_DEVICE_NOT_MOUNTED);
  420.                  break;
  421.                }
  422.             }
  423.  
  424.             /* Check write-prot, block length */
  425.             if(x=TapeIO(MDSNS,0,CTLWAIT)) DoSense(x);
  426.             else {
  427.                if(opmode==WRITING && (sns[2] & 0x80)) {
  428.                  freestuff();
  429.                  opmode=CLOSED;
  430.                  returnpkt(DOSFALSE,ERROR_DISK_WRITE_PROTECTED);
  431.                  break;
  432.                }
  433.                if(sns[3] >= 8) {
  434.                  x = *((long *) &sns[8]) & 0x00ffffff;  /* get block len */
  435.                  if(x != blksize) {  /* set block size with Mode Select */
  436.                    Mprintf("Current block size = %lu\n",x);
  437.                    if(x=TapeIO(MDSET,varblk,CTLWAIT)) DoSense(x);
  438.                    else TapeIO(TSENSE,0,CTLWAIT);
  439.                  }
  440.                }
  441.             }
  442.  
  443.             /* position the medium at starting spot */
  444.             if(position==POSNREW) gotopos=blknum;
  445.             if(postape(position,fmarks,gotopos)) goto GIVEUP;
  446.  
  447.             /* Get capacity for 3M tape */
  448.             if(DAC) {
  449.                tpsize = 0x7fffffff;
  450.                if(TapeIO(RDCAP,0,CTLWAIT)) TapeIO(TSENSE,0,CTLWAIT);
  451.                else {
  452.                  tpsize = (((long)sns[2] << 8) | sns[3]) + 1;
  453.                  Mprintf("Capacity: %ld blocks\n", tpsize);
  454.                }
  455.                Mprintf("%s Opened at block %lu\n",devname,blknum);
  456.             }
  457.             else Mprintf("%s Opened at file %ld\n",devname,filenum);
  458.           }
  459.           FASTOPEN:
  460.           dirty=FALSE; inprog=FALSE; Boff=0; pkcnt=0; tapenum=1;
  461.           iostat=0;  Bn=0;  remain=TBSize;  current=0;  atend=eot=rderr=FALSE;
  462.           returnpkt(DOSTRUE,0);
  463.           break;
  464.  
  465.  
  466.      case ACTION_END:        /*----------- Close() -----------*/
  467.           dcnt=DOSTRUE; mcnt=0; iostat=0;
  468.           switch(opmode) {
  469.              case WRITING:
  470.                 if(inprog) iostat = TapeIO(TFINISH,0,CTLWAIT);
  471.                 if(iostat) iostat = wrteot(Bn^1,1,iostat);
  472.                 if(iostat==0) {
  473.                    if(dirty) {
  474.                       memset(TapeBuff[Bn]+Boff,0,remain);
  475.                       iostat = TapeIO(TWRITE,Bn,CTLWAIT);
  476.                    }
  477.                    if(iostat) iostat = wrteot(Bn,0,iostat);
  478.                 }
  479.                 if(iostat) { dcnt=DOSFALSE; Mprintf("Error during write\n"); }
  480.                 else if(pkcnt) {
  481.                    blknum += actual;  x=0;
  482.                    if(SEQ) {
  483.                       x=TapeIO(WFMARK,fmarks,CTLWAIT);
  484.                       lastwrtn = filenum++;
  485.                    } else filenum = blknum;
  486.                    if(x) DoSense(x);
  487.                 }
  488.                 break;
  489.              case READING:
  490.                 if(inprog) iostat = TapeIO(TFINISH,0,CTLWAIT);
  491.                 if(iostat) { if(FMK==DoSense(iostat)) atend=TRUE; }
  492.                 if(DAC) filenum = blknum;
  493.                 else    filenum = atend ? filenum+1 : -1 ;
  494.                 lastwrtn = -1;
  495.                 break;
  496.              case RAWMODE:
  497.                 if(x=TapeIO(RAWCMD,Bn,CTLWAIT)) DoSense(x); break;
  498.              case UMODSEL:
  499.                 if(x=TapeIO(USRMODE,Bn,CTLWAIT)) DoSense(x); break;
  500.              case CLOSED:
  501.                 dcnt=DOSFALSE;  mcnt=ERROR_ACTION_NOT_KNOWN; break;
  502.              case CURRPOS:  break;
  503.           }
  504.           returnpkt(dcnt,mcnt);
  505.           freestuff();
  506.           if(starting) starting=FALSE;
  507.           else if(opmode<=WRITING) {
  508.                    Mprintf("Closed at block %lu, next file= %ld\n\n",
  509.                       blknum,filenum);
  510.                    if(rewcl) if(x=(TapeIO(TREWIND,0,CTLWAIT))) DoSense(x);
  511.                    if(eject) if(x=(TapeIO(TEJECT,0,CTLWAIT))) DoSense(x);
  512.                }
  513.           opmode=CLOSED;
  514.           break;
  515.  
  516.      case ACTION_READ:       /*------- Read(), Seek() --------*/
  517.      case ACTION_SEEK:
  518.           if(opmode!=READING && opmode!=CURRPOS) {
  519.               Mprintf("Function/mode error\n");
  520.               res1 = -1;
  521.               goto RDEND;
  522.           }
  523.           if(opmode==CURRPOS && !atend) {  /* reading file position */
  524.               remain = 1 + sprintf(TapeBuff[Bn],"%ld",filenum);
  525.               dirty=atend=TRUE;
  526.           }
  527.           if(mypkt->dp_Type == ACTION_READ) {
  528.               dptr = (UBYTE *) mypkt->dp_Arg2;
  529.               dcnt = res1 =    mypkt->dp_Arg3;
  530.               rcnt = 0;
  531.           }
  532.           else {  /* ACTION_SEEK */
  533.               if(opmode==CURRPOS) goto BADSEEK;
  534.               rcnt = -1;
  535.               dcnt = mypkt->dp_Arg2;  /* offset */
  536.               x    = mypkt->dp_Arg3;  /* type   */
  537.               if(atend || eot || varblk) goto BADSEEK;
  538.               if(x==OFFSET_END) goto BADSEEK;
  539.               if(x==OFFSET_BEGINNING) dcnt -= current;
  540.               /* x==OFFSET_CURRENT, dcnt is correct */
  541.               res1 = current;
  542.               if(dcnt<0) { /* backwards seek, must be to BOF */
  543.                 if((dcnt+current)==0) {
  544.                    if(SEQ && position == POSNREW) goto BADSEEK;
  545.                    if((dcnt+Boff)<0) {  /* still in buffer? */
  546.                       if(inprog) TapeIO(TFINISH,0,CTLWAIT);
  547.                       if(postape(position,fmarks,gotopos)) goto BADSEEK;
  548.                       dirty=FALSE;  Bn=0;
  549.                    }
  550.                    current=0;  dcnt=0;  Boff=0;  remain=TBSize;
  551.                 }
  552.                 else {
  553.                 BADSEEK:
  554.                    Mprintf("Disallowed seek type=%ld off=%ld curr=%ld\n",
  555.                             x,dcnt,current);
  556.                    res1 = -1;
  557.                    goto RDEND;
  558.                 }
  559.               }
  560.           }
  561.           while(dcnt) {
  562.             if(!dirty) {  /* need to prefetch first buffer */
  563.                iostat = TapeIO(TREAD,Bn,CTLWAIT);
  564.                remain=0;  Bn=1;  dirty=TRUE;
  565.             }
  566.             if(remain==0)  {  /* current buffer exhausted */
  567.                if(rderr) {
  568.                   Mprintf("Error during read\n");
  569.                   if(errec)
  570.                      if(ask("  Ignore READ ERROR?", "IGNORE", "FAIL"))
  571.                         rderr=FALSE;
  572.                   if(rderr) { res1 = -1; goto RDEND; }
  573.                }
  574.                if(eot) {  /* end-of-tape */
  575.                   TapeIO(TREWIND,0,CTLWAIT);
  576.                   if(NewTape())  {
  577.                      iostat=TapeIO(TREAD,(Bn^1),CTLWAIT);
  578.                      eot=atend=FALSE;
  579.                   }
  580.                   else atend=TRUE;
  581.                }
  582.                if(atend)  { res1 = rcnt;  goto RDEND; }
  583.                if(inprog)  iostat = TapeIO(TFINISH,0,CTLWAIT);
  584.                if(iostat)    /* check status of previous read */
  585.                   switch(DoSense(iostat)) {
  586.                     case EOM: eot=TRUE;   /* & fall thru */
  587.                     case FMK: atend=TRUE; /* & fall thru */
  588.                     case RCV: break;
  589.                     default:  rderr=TRUE;
  590.                   }
  591.                remain = actual;
  592.                blknum += actual/blksize;  /* numblks; */
  593.                if(!(atend || eot || rderr))  /* start refilling this buffer */
  594.                    iostat = TapeIO(TREAD,Bn,Ctl);
  595.                Bn ^= 1;      /* switch to other (full) buffer */
  596.                Boff = 0;
  597.                if(bswap) swapbytes(TapeBuff[Bn]);
  598.             }
  599.             mcnt = (dcnt>remain) ? remain : dcnt;
  600.             if(mypkt->dp_Type == ACTION_READ)  {
  601.                memcpy (dptr, TapeBuff[Bn]+Boff, mcnt);
  602.                rcnt += mcnt ;
  603.             }
  604.             dcnt -= mcnt ;
  605.             Boff += mcnt ;
  606.             dptr += mcnt ;
  607.             current += mcnt;
  608.             remain -= mcnt ;
  609.           }
  610.           RDEND:
  611.           returnpkt(res1,0);
  612.           break;
  613.  
  614.  
  615.      case ACTION_WRITE:      /*----------- Write() -----------*/
  616.           if(opmode < WRITING) {
  617.             Mprintf("Function/mode error\n");
  618.             res1 = -1;
  619.             goto WRTEND;
  620.           }
  621.           pkcnt++;
  622.           dptr = (UBYTE *) mypkt->dp_Arg2;
  623.           dcnt = res1 =    mypkt->dp_Arg3;
  624.           while (dcnt)  {
  625.             if(dcnt >= remain) {
  626.                memcpy (TapeBuff[Bn]+Boff, dptr, remain);
  627.                if(inprog) iostat = TapeIO(TFINISH,0,CTLWAIT);
  628.                if(iostat)    /* check status of previous write */
  629.                  if(iostat = wrteot(Bn^1,1,iostat)) {  /* possible EOT */
  630.                     Mprintf("Error during write\n");
  631.                     if(errec)
  632.                        if(ask("  Ignore WRITE ERROR?", "IGNORE", "FAIL"))
  633.                           iostat=0;
  634.                     if(iostat) {
  635.                        res1 = -1;
  636.                        iostat=0;  dirty=FALSE;  pkcnt=0;
  637.                        goto WRTEND;
  638.                     }
  639.                  }
  640.                Boff += remain;
  641.                iostat = TapeIO(TWRITE,Bn,Ctl); /* start writing full buffer */
  642.                blknum += actual;
  643.                dcnt -= remain;
  644.                dptr += remain;
  645.                Boff  = 0;
  646.                remain= TBSize;
  647.                Bn   ^= 1;      /* switch to other (empty) buffer */
  648.                dirty = FALSE;
  649.             }
  650.             else {
  651.                memcpy (TapeBuff[Bn]+Boff, dptr, dcnt);
  652.                remain -= dcnt;
  653.                Boff += dcnt;
  654.                dcnt  = 0;
  655.                dirty = TRUE;
  656.             }
  657.           }
  658.           WRTEND:
  659.           returnpkt(res1,0);
  660.           break;
  661.  
  662.  
  663.      case ACTION_IS_FILESYSTEM:  /* DOS 2+ only */
  664.           dos2 = TRUE;
  665.           returnpkt(DOSFALSE,0);
  666.           break;
  667.  
  668.      case ACTION_CURRENT_VOLUME:
  669.           returnpkt(dvnode,0);
  670.           break;
  671.  
  672.      case ACTION_FLUSH:   /* flush buffers, NOOP */
  673.           returnpkt(DOSTRUE,0);
  674.           break;
  675.  
  676.      case ACTION_LOCATE_OBJECT:  /* lock */
  677.           if(dos2) returnpkt(DOSFALSE,ERROR_ACTION_NOT_KNOWN);
  678.           else     returnpkt(mypkt->dp_Arg1,0);
  679.           break;
  680.  
  681.      case ACTION_FREE_LOCK:      /* unlock */
  682.           if(dos2) returnpkt(DOSFALSE,ERROR_ACTION_NOT_KNOWN);
  683.           else     returnpkt(DOSTRUE,0);
  684.           break;
  685.  
  686.      case ACTION_SET_PROTECT:  /* SetProtection */
  687.      case ACTION_DISK_INFO:    /* info */
  688.           returnpkt(DOSFALSE,ERROR_ACTION_NOT_KNOWN);
  689.           break;
  690.  
  691.      default:  /* others not supported */
  692.           returnpkt(DOSFALSE,ERROR_ACTION_NOT_KNOWN);
  693.           Mprintf("Unsupported_Pkt=%ld\n",mypkt->dp_Type);
  694.  
  695.      } /* end of switch */
  696.  } /* end of loop */
  697. } /* end of _main() */
  698.  
  699. /**************************************************************************/
  700.  
  701. int DoSense(long iocode)
  702. {
  703.    UBYTE snskey;
  704.    static char *snstext[] = {
  705.        "NONE" , "RECOVERED ERROR", "NOT READY", "MEDIUM ERROR",
  706.        "HARDWARE ERROR", "ILLEGAL REQUEST", "UNIT ATTENTION", "DATA PROTECT",
  707.        "BLANK CHECK", "KEY=9", "COPY ABORTED", "ABORTED COMMAND", "KEY=C",
  708.        "VOLUME OVERFLOW", "KEY=E", "KEY=F", "FILEMARK", "END-OF-MEDIUM",
  709.        "INCORRECT BLOCK LENGTH"
  710.    };
  711.    static char *sderror[] = {
  712.        "SELF-UNIT", "DMA", "PHASE", "PARITY", "SELECT-TIMEOUT", "BADSTATUS"
  713.    };
  714.  
  715.    if(iocode==0 || iocode==HFERR_BadStatus)  {
  716.        TapeIO(TSENSE,0,CTLWAIT);
  717.        if((sns[0] & 0x70)==0x70) {  /* extended */
  718.           snskey = sns[2] & 0x0f;  /* real sense key */
  719.           if(snskey==0) {          /* pseudo sense keys */
  720.               if(sns[2] & 0x20) snskey = ILI;
  721.               if(sns[2] & 0x80) snskey = FMK;
  722.               if(sns[2] & 0x40) snskey = EOM;
  723.           }
  724.        }
  725.        else snskey = sns[0] & 0x0f; /* non-extended */
  726.        linktp->sense = snstext[snskey];  /* keep last error info */
  727.        linktp->xsns1 = sns[12];
  728.        linktp->xsns2 = sns[13];
  729.        linktp->sns = sns;  /* flag for tapemon to print all data */
  730.        Mprintf("Sense: %s, other= %02X,%02X\n", snstext[snskey], sns[12],sns[13]);
  731.        linktp->sns = NULL;
  732.        return((int)snskey);
  733.    }
  734.    else if(iocode==FAKEOM) return(EOM);  /* DAC only */
  735.    else {
  736.        if(iocode>=40 && iocode<=45)
  737.             Mprintf("SCSI %s ERROR\n",sderror[iocode-40]);
  738.        else Mprintf("SCSI DRIVER ERROR CODE %lu\n",iocode);
  739.        return(SERR);
  740.    }
  741. }
  742.  
  743. /**************************************************************************/
  744. /* postape():  position the tape medium at the place we want to open it
  745. */
  746.  
  747. int postape(int how, int fm, long where)
  748. {
  749.  long x;  int y=0;  BOOL slow=TRUE;
  750.    switch(how) {
  751.     case POSNREW:            /* TAPE:*  */
  752.        if(filenum>=0) {
  753.           if(SEQ) blknum = 0;
  754.           break;
  755.        } /* else pos unknown, fall thru and rewind */
  756.     case POSREW:             /* TAPE:   */
  757.        if(x=TapeIO(TREWIND,0,CTLWAIT)) y=DoSense(x);
  758.        blknum = reserved;
  759.        break;
  760.     case POSAPP:            /* TAPE:APP */
  761.        if(x=TapeIO(TSKIPE,0,CTLWAIT)) y=DoSense(x);
  762.        if(lastwrtn<0) filenum = -1;
  763.        slow=FALSE; /* & fall thru */
  764.     case POSEND:            /* TAPE:END */
  765.        if((slow && lastwrtn<0) || y==ILL) {
  766.           /* drive doesn't support skip-eod, or tape:end */
  767.           y=0;
  768.           if(!slow) Mprintf("(doing it the hard way)\n");
  769.           if(x=TapeIO(TREWIND,0,CTLWAIT)) DoSense(x);
  770.           while(y==0) {   /* skip files until we hit a blank spot */
  771.              Mprintf("Skipping over file %ld\n",filenum);
  772.              if(x=TapeIO(TSKIPF,fm,CTLWAIT)) y=DoSense(x);
  773.              if(y==FMK) y=0;
  774.              filenum++;
  775.           }
  776.           if(y==BLC) { y=0;  filenum--; }
  777.        }
  778.        blknum = 0;
  779.        break;
  780.     case POSSKIP:            /* TAPE:n */
  781.        if(DAC) blknum = where;
  782.        else { /* SEQ */
  783.          if(x=TapeIO(TREWIND,0,CTLWAIT)) { y=DoSense(x); break; }
  784.          if(fm==0) break;
  785.          Mprintf("Skipping to file %ld\n",where);
  786.          if(x=TapeIO(TSKIPF,where*fm,CTLWAIT)) y=DoSense(x);
  787.          if(y==FMK) y=0;
  788.          blknum = 0;
  789.        }
  790.        filenum = where;
  791.        lastwrtn = -1;
  792.        break;
  793.    }
  794.    if(y) Mprintf("Unable to position tape\n");
  795.    return(y);
  796. }
  797.  
  798. /**************************************************************************/
  799. /* wrteot:  handle messiness that happens when writing at end of tape.
  800.       Returns non-zero status if not EOT or tape swap unsuccessful. */
  801.  
  802. long wrteot(int bfn, int dec, long stat)
  803. {
  804.    extern long actual;
  805.    long saveboff, nn;
  806.    long residue=0;
  807.    long ios = stat;
  808.    int  s = DoSense(ios);
  809.    if(s==EOM || s==VOF) {   /* EOT? */
  810.       if(s==VOF && (sns[0]&0xF0)==0xF0) residue = ((long)sns[5]<<8) + sns[6];
  811.       if(residue) Mprintf("Holding %ld unwritten blocks\n",residue);
  812.       if(residue>numblks) { Mprintf("OOPS, data lost!\n"); residue=0; }
  813.       TapeIO(TREWIND,0,CTLWAIT);
  814.       if(NewTape()) {       /* ask for new tape */
  815.          /* shift unwritten bytes to start of buffer and rewrite on new tape */
  816.          if(residue) {
  817.             nn = (numblks-residue)*blksize;
  818.             saveboff = Boff;
  819.             Boff = residue*blksize;
  820.             if(nn) memmove(TapeBuff[bfn],TapeBuff[bfn]+nn,Boff);
  821.             blknum = reserved;
  822.             ios=TapeIO(TWRITE,bfn,CTLWAIT);
  823.             if(ios) s=DoSense(ios);
  824.             blknum += actual;
  825.             Boff = saveboff;
  826.          }
  827.          else ios=0;
  828.       }
  829.    }
  830.    if(s==RCV) ios=0;  /* ignore recoveries */
  831.    return(ios);
  832. }
  833.  
  834. /**************************************************************************/
  835.  
  836. void freestuff(void)
  837. {
  838.   if(TapeBuff[0]) FreeMem(TapeBuff[0],TBSize);
  839.   if(TapeBuff[1]) FreeMem(TapeBuff[1],TBSize);
  840.   TapeBuff[0] = TapeBuff[1] = NULL;
  841.   return;
  842. }
  843.  
  844. /**************************************************************************/
  845. /* getstart:
  846.     Given pointer (z) to a string of the form "SS-nn/..."
  847.     returns a number corresponding to SS, the binary value of nn,
  848.     and the pointer is updated to point after the slash.
  849. */
  850. int getstart(long *num)
  851. {
  852.   extern char *z;
  853.   #define NOTFND 99
  854.   #define ENDLST 100
  855.   #define NKEYS  19  /* number of keywords */
  856.   static char *keys[NKEYS] =
  857.   { "UN","BS","NB","RB","BT","FM","ER","OV","DF","C9",
  858.   /* 0    1    2    3    4    5    6    7    8    9 */
  859.  
  860.     "NR","RT","RC","EC","SW","RO","AO","MR","DB"};
  861.   /* 10   11   12   13   14   15   16   17   18 */
  862.   char *ii, *jj;  int kk;
  863.  
  864.   if (*z == '\0') return(ENDLST);      /* return if end of string */
  865.   for (ii=z;  *ii != '-'; ii++);       /* find the dash */
  866.   for (jj=z;  *jj != '/' && *jj !='\0'; jj++); /* find the slash */
  867.   *ii = '\0'; *jj = '\0';              /* null-terminate name & number */
  868.   *num = (long) strtoul (++ii,NULL,0); /* return converted number */
  869.   ii = z;     z = ++jj;                /* update ptr to next item */
  870.   for (kk=0; kk <= NKEYS; kk++)  {     /* search for keyword */
  871.      if (*ii == *keys[kk] && *(ii+1) == *(keys[kk]+1) )
  872.         return(kk);                    /* return index of keyword if found */
  873.   }
  874.   return(NOTFND);                      /* didn't find keyword */
  875. }
  876.  
  877. /**************************************************************************
  878.    Mprintf requests that the TapeMon program print the message in 'dbb'.
  879.     Since this handler cannot do DOS I/O, it must beg the TapeMon program,
  880.     possibly running in a CLI somewhere, to do the printf for it.  If the
  881.     TapeMon is running, it will have sent us a message containing the port
  882.     pointer for TapeMon, so we can send messages to it.
  883. */
  884.  
  885. void  Mprintf(char *format, ...)
  886. {
  887.   va_list ap;
  888.  
  889.    va_start(ap,format);
  890.    if(monport) {
  891.       if(mbusy) {   /* still waiting for reply from tapemon? Wait.*/
  892.          WaitPort(btnport);
  893.          GetMsg(btnport);
  894.       }
  895.       vsprintf(linktp->dbb,format,ap);
  896.       monmsg.mptr = linktp;
  897.       PutMsg(monport,&monmsg.bmsg);
  898.       mbusy=TRUE;
  899.    }
  900.    va_end(ap);
  901.    return;
  902. }
  903.  
  904. /**************************************************************************/
  905. /* toUPPER: convert string to upper case */
  906.  
  907. void toUPPER(char *zz)
  908. {
  909.   for(; *zz != '\0'; zz++)  if(*zz >= 'a') *zz -= 0x20;
  910.   return;
  911. }
  912.  
  913. /**************************************************************************
  914. /* swapbytes: swap byte pairs */
  915.  
  916. void swapbytes(char *zz)
  917. {
  918.    char x;
  919.    int n;
  920.    long len = TBSize/2;
  921.    for(n=0; n<len; n++) {
  922.      x = *zz;
  923.      *zz = *(zz+1);
  924.      *(zz+1) = x;
  925.      zz += 2;
  926.    }
  927.    return;
  928. }
  929.  
  930. /**************************************************************************
  931. *  NewTape()   Handles transition to a new tape.
  932. */
  933.  
  934. long NewTape(void)
  935. {
  936.  long choice,s,t,v;
  937.  static char rqm[40];
  938.  Mprintf("Time to insert tape# %d\n",++tapenum);
  939.  sprintf(rqm," Insert tape %d for %s",tapenum,devname);
  940.  do {
  941.     s=NOS;
  942.     choice= ask(rqm," Continue ", " Quit ");
  943.     if(choice)
  944.       do {
  945.         if(t=TapeIO(TREADY,0,CTLWAIT)) s=DoSense(t);  /* eat unit-atten */
  946.         else s=NOS;
  947.         if(s==UAT && reten) if(v=TapeIO(TRETEN,0,CTLWAIT)) DoSense(v);
  948.       } while(s==UAT);
  949.  } while(s!=NOS);
  950.  if(choice) {
  951.     if(s=TapeIO(TREWIND,0,CTLWAIT)) DoSense(s);
  952.     if(DAC) blknum=reserved;  /* reset block number */
  953.  }
  954.  return(choice);
  955. }
  956.  
  957. /**************************************************************************
  958. *  ask()   Displays a yes/no requester.
  959. */
  960.  
  961. long ask(char *txt, char *yes, char *no)
  962. {
  963. #define ITXT { AUTOFRONTPEN,AUTOBACKPEN,AUTODRAWMODE,AUTOLEFTEDGE, \
  964.                   AUTOTOPEDGE, AUTOITEXTFONT, NULL, NULL }
  965.  static struct IntuiText rtxt[3] = { ITXT, ITXT, ITXT };
  966.  if(!IntuitionBase) return(0);
  967.  rtxt[0].IText = (UBYTE *) txt;
  968.  rtxt[1].IText = (UBYTE *) yes;
  969.  rtxt[2].IText = (UBYTE *) no;
  970.  return(AutoRequest(NULL,&rtxt[0],&rtxt[1],&rtxt[2],NULL,NULL,300,50));
  971. }
  972.  
  973.  
  974. /**************************************************************************
  975.  * returnpkt() - reply DOS packet.
  976.  *   Adapted from "misc.c" code by Phillip Lindsay (C) Commodore 1986
  977. */
  978.  
  979. void returnpkt(ULONG res1, ULONG res2)
  980. {
  981.  struct Message *mess;
  982.  struct MsgPort *replyport;
  983.  
  984. #ifdef DBUG
  985.   if(dbug) Mprintf("REPLY  %08X %08X\n", res1, res2);
  986. #endif
  987.  
  988.  mypkt->dp_Res1          = res1;
  989.  mypkt->dp_Res2          = res2;
  990.  replyport               = mypkt->dp_Port;
  991.  mess                    = mypkt->dp_Link;
  992.  mypkt->dp_Port          = &myproc->pr_MsgPort;
  993.  mess->mn_Node.ln_Name   = (char *) mypkt;
  994.  mess->mn_Node.ln_Succ   = NULL;
  995.  mess->mn_Node.ln_Pred   = NULL;
  996.  
  997.  PutMsg(replyport,mess);
  998. }
  999.  
  1000.